/**
* @brief 程序固化模块
* @author chenjl
* @date 2019.07.15
*/

#include "stm32f4xx.h"
#include "stm32f4xx_flash.h"

#define FLASH_SECTOR_NUM	(20+3)
extern const long Flash_Sector_Table[FLASH_SECTOR_NUM][2];
static uint8_t	FLASH_SECTOR_ERASED_FLAG[FLASH_SECTOR_NUM] = {0};

uint8_t  getUpdateChar(void);
uint8_t  isCharPresent(void);
void putChar(uint8_t ch);

typedef struct {
	short  cmd;		//0=read,1=write,2=rerase flash,0xf1=exit to usr app
	short  param;	//read/write size,in byte,and must muliply 4
	long	addr;	//start addr; aligned 4
	long   buff[1];	//dynamic buffsize;
}PACKET_STR;

static void SendPakcet(uint8_t *buff, int len);
static PACKET_STR *ReadPacket(void);
static int8_t AddressSector(long addr);	
static uint8_t  burnEchoBuff[1024];

void BurnMain(void)
{
	PACKET_STR *pcmd = 0;
	long *tmpaddr = 0;
	long size = 0;
	PACKET_STR *pecho = (PACKET_STR *)burnEchoBuff;
	long cpyidx = 0;
	int8_t	sectoridx = 0;
	
	while(1)
	{
		pcmd = ReadPacket();
		if(pcmd != 0)
		{
			if(pcmd->cmd == 0)			//读取一段内存
			{
				tmpaddr = (long*)(pcmd->addr & (~3));
				size = pcmd->param;
				if(size > 128) size = 128;
				pecho->cmd = 0;
				pecho->addr = (long)tmpaddr;	

				cpyidx = 0;
				while(size >= 4)
				{
					if(AddressSector((long)(tmpaddr + cpyidx)) >= 0)
					{
						pecho->buff[cpyidx] = tmpaddr[cpyidx];
						cpyidx++;
					}
					size -= 4;
				}
				pecho->param = cpyidx * 4;
				
				SendPakcet((uint8_t*)pecho,pecho->param + 8);
			}
			else if(pcmd->cmd == 1)		//写一段内存
			{
				tmpaddr = (long*)(pcmd->addr & (~3));
				size = pcmd->param;
				
				cpyidx = 0;
				FLASH_Unlock();
				
				while(size >= 4)
				{
					sectoridx = AddressSector((long)(tmpaddr + cpyidx));
					if( sectoridx >= 0)
					{
						if(FLASH_SECTOR_ERASED_FLAG[sectoridx] == 0)
						{
							FLASH_EraseSector(FLASH_Sector_1+sectoridx*8,VoltageRange_3);
							FLASH_SECTOR_ERASED_FLAG[sectoridx] = 1;
						}
						FLASH_ProgramWord((long)(tmpaddr+cpyidx),pcmd->buff[cpyidx]);						
						cpyidx++;
					}
					size -= 4;
				}
				FLASH_Lock();
				
				//回复数据包
				pecho->cmd = 1;
				pecho->addr = (long)tmpaddr;
				pecho->param = cpyidx * 4;
				SendPakcet((uint8_t*)pecho,0 + 8);	
			}
			else if(pcmd->cmd == 2)		//插队片上FLASH
			{
				for(cpyidx=0;cpyidx<FLASH_SECTOR_NUM;cpyidx++)
				{
					FLASH_SECTOR_ERASED_FLAG[cpyidx] = 0;
				}
				pecho->cmd = 2;
				pecho->param = 0;
				SendPakcet((uint8_t*)pecho,pecho->param + 4);				
			}
			else if(pcmd->cmd ==3)		//查询BOOTLOADER软件版本
			{
				/*版本查询*/
				pecho->cmd = 3;
				pecho->param = 116;
				SendPakcet((uint8_t*)pecho,0 + 8);
			}
			else if(pcmd->cmd == 0xf1)		//退出BOOTLOADER ，运行应用程序
			{
				pecho->cmd = 0xf1;
				pecho->param = 0;
				SendPakcet((uint8_t*)pecho,0 + 4);
				return;
			}
		}
	}
}


static int8_t AddressSector(long addr)
{
	int8_t i = 0;
	for (i = 0 ; i < FLASH_SECTOR_NUM; i++)
	{
		if((addr >= Flash_Sector_Table[i][0]) && 
			addr <= Flash_Sector_Table[i][1])
		{
			return i;
		}
	}
	return -1;
}

static uint8_t uart1_rcvbuff[4*128];

static PACKET_STR *ReadPacket(void)
{
	uint8_t bExit = 0;
	uint8_t	lena = 0;
	uint8_t	crca = 0;
	uint8_t	crc  = 0;
	uint8_t  ch = 0;
	uint8_t	stat = 0;
	int	cpycnt = 0;
	
	do
	{		
		if(isCharPresent())
		{
			ch = getUpdateChar();
			
			switch(stat)
			{
				case 0:			//查找包头，非包头数据则直接返回其值
					if(ch == 0xaa)
					{
						stat = 1;
					}
					else
					{
						putChar(ch);
					}
					break;
				case 1:			//包长
					lena = ch;
					stat = 2;
					break;
				case 2:			//包长的校验码
					if((ch + lena) == 255)
					{
						stat = 3;
					}
					else
					{
						stat = 0;
					}
					break;
				case 3:			//数据校验码
					crca = ch;					
					crc = 0;
					if(lena > 0)
					{
						cpycnt = 0;
						stat = 4;
					}
					else
					{
						stat = 0;
					}
					break;
				case 4:			//数据区拷贝
					uart1_rcvbuff[cpycnt++] = ch;
					crc += ch;
					if(cpycnt >= lena)
					{
						if(crc == crca)
						{
							return (void*)uart1_rcvbuff;
						}
						stat = 0;
					}
					break;
				default:
					stat = 0;
					break;
			}
		}		
	}while(bExit == 0);
	return 0;
}

static void SendPakcet(uint8_t *buff, int len)
{
	int i = 0;
	uint8_t crc = 0;
	for(i = 0; i < len;i++)
	{
		crc += buff[i];
	}
	
	putChar(0xaa);
	putChar((uint8_t)len);
	putChar((uint8_t)(~len));
	putChar(crc);
	
	for(i=0;i<len;i++)
	{		
		putChar(buff[i]);
	}
}

